package docbookarchivemanager.utils;

import java.util.Iterator;
import java.util.HashMap;
import java.util.UUID;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;

/**
 * <p>
 * Minimum implementation of NamespaceContext.</p>
 *
 * <p>
 * Assumes "<em>static</em>" namespace, i.e. NamespaceContext is not updated
 * during parsing. Also assumes "<em>flat</em>" namespace, i.e. only one prefix
 * per Namespace URI.</p>
 */
public class NamespaceContextImpl implements NamespaceContext {

    private static final boolean DEBUG = false;

    private static HashMap<String, String> prefixToNamespaceURI = new HashMap<>();

    private static HashMap<String, String> namespaceURIToPrefix = new HashMap<>();

    /**
     * <p>
     * Bind a prefix to a Namespace URI.</p>
     *
     * @param prefix Prefix to bind to <code>namespaceURI</code>.
     * @param namespaceURI Namespace URI to bind to <code>prefix</code>.
     */
    public static void bindPrefixToNamespaceURI(String prefix, String namespaceURI) {

        if (DEBUG) {
            System.err.println("bindPrefixToNamespaceURI(" + prefix + ", "
                    + namespaceURI + ")");
        }

        prefixToNamespaceURI.put(prefix, namespaceURI);
        namespaceURIToPrefix.put(namespaceURI, prefix);
    }

    /**
     * <p>
     * Get the Namespace URI bound to the prefix.</p>
     *
     * @param prefix Lookup Namespace URI that is bound to this prefix.
     *
     * @return Namespace URI bound to <code>prefix</code>.
     */
    @Override
    public String getNamespaceURI(String prefix) {

        if (DEBUG) {
            System.err.println("getNamespaceURI(" + prefix + ")");
        }

        if (prefix == null) {
            throw new IllegalArgumentException(
                    "NamespaceContextImpl#getNamespaceURI(String prefix) with prefix == null");
        }

        // constants
        if (prefix.equals(XMLConstants.XML_NS_PREFIX)) {
            return XMLConstants.XML_NS_URI;
        }
        if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) {
            return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
        }

        // default
        if (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) {
            if (prefixToNamespaceURI.containsKey(prefix)) {
                return (String) prefixToNamespaceURI.get(prefix);
            } else {
                return XMLConstants.NULL_NS_URI;
            }
        }

        // bound
        if (prefixToNamespaceURI.containsKey(prefix)) {
            return (String) prefixToNamespaceURI.get(prefix);
        }

        // unbound
        return XMLConstants.NULL_NS_URI;
    }

    /**
     * <p>
     * Get the prefix bound to the Namespace URI.</p>
     *
     * @param namespaceURI Lookup prefix that is bound to this Namespace URI.
     *
     * @return prefix bound to this Namespace URI.
     */
    @Override
    public String getPrefix(String namespaceURI) {

        if (DEBUG) {
            System.err.println("getPrefix(" + namespaceURI + ")");
        }

        if (namespaceURI == null) {
            throw new IllegalArgumentException(
                    "NamespaceContextImpl#getPrefix(String namespaceURI) with namespaceURI == null");
        }

        // constants
        if (namespaceURI.equals(XMLConstants.XML_NS_URI)) {
            return XMLConstants.XML_NS_PREFIX;
        }
        if (namespaceURI.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
            return XMLConstants.XMLNS_ATTRIBUTE;
        }

        // bound
        if (namespaceURIToPrefix.containsKey(namespaceURI)) {
            return (String) namespaceURIToPrefix.get(namespaceURI);
        }

        // mimic "default Namespace URI"
        if (namespaceURI.equals(XMLConstants.NULL_NS_URI)) {
            return XMLConstants.DEFAULT_NS_PREFIX;
        }

        // unbound
        return null;
    }

    /**
     * <p>
     * Get prefixes bould to Namespace URI.</p>
     *
     * <p>
     * TODO: not implemented. Throws UnsupportedOperationException.</p>
     *
     * @param namespaceURI Lookup prefixes bound to this Namespace URI
     *
     * @return Iterator over prefixes bound to this Namespace URI.
     *
     * @throws UnsupportedOperationException When called.
     */
    @Override
    public Iterator getPrefixes(String namespaceURI) {

        if (DEBUG) {
            System.err.println("getPrefixes(" + namespaceURI + ")");
        }

        throw new UnsupportedOperationException(
                "NamespaceContextImpl#getPrefixes(String namespaceURI)"
                + " not implemented");
    }

    public static void bindDefaultNamespaces() {
        bindPrefixToNamespaceURI("darElements", "http://docbook-archive.sf.net/ns/elements");
        bindPrefixToNamespaceURI("darManifest", "http://docbook-archive.sf.net/ns/manifest");
    }

    private static boolean containsNamespace(String ns) {
        return namespaceURIToPrefix.containsKey(ns);
    }

    public static String getNamespacePrefix(String ns) {
        if (containsNamespace(ns)) {
            return namespaceURIToPrefix.get(ns);
        } else {
            return generateRandomPrefix();
        }
    }

    private static String generateRandomPrefix() {
        String prefix;
        do {
            prefix = UUID.randomUUID().toString().substring(0, 8);
        } while (containsNamespace(prefix));

        return prefix;
    }
}
